<?php

/**
 * Предоставляет централизованный доступ к всем настройкам
 */
class Regedit implements iRegedit {

	private
		$aRelations = array (),
		$aValues    = array (),
		$aIdsCache  = array (),
		$aHelpers   = null,
		$init       = false;
	
	private static $instance = false;

	/**
	 * Возвращает объект класса Regedit
	 * @return Regedit
	 */
	public static function getInstance () {
		if (self::$instance === false) {
			self::$instance = new Regedit;
		}
		return self::$instance;
	}

	private function __construct () {}

	/**
	 * Возвращает значение переменной реестра
	 * @param string $key ключ вида //родитель1/родитель2/переменная
	 * @return string
	 */
	final public function get ($key) {
		if (!$this->init) {
			$this->init ();
		}
		
		if (mb_substr ($key, 0, 2) == '//') {
			$key = mb_substr ($key, 2);
		}
		
		if (!$paramId = $this->getId ($key)) {
			return '';
		}
		
		if (isset ($this->aValues[$paramId])) {
			return $this->aValues[$paramId];
		}
		
		return '';
	}

	/**
	 * Записывает значение переменной реестра
	 * @param string $key ключ вида //родитель1/родитель2/переменная
	 * @param mixed $value значение
	 * @param boolean
	 */
	final public function set ($key, $value = false, $help = false) {
		if (!$this->init) {
			$this->init ();
		}
		
		if (mb_substr ($key, 0, 2) == '//') {
			$key = mb_substr ($key, 2);
		}
		
		$key = trim ($key, '/');
		
		$value = $value === false ? 'NULL' : '\'' . safeSql($value) . '\'';
			
		if ($id = $this->getId ($key)) {
			$help = $help === false ? '' : ', `help` = \'' . safeSql($help) . '\'';
			
			$db = DBController::getInstance ();
			$db->query ("UPDATE `explay_regedit` SET `value` = " . $value . $help . " WHERE `id` = " . $id);
			$db->query ("COMMIT");
		} else {
			$help = $help === false ? 'NULL' : '\'' . safeSql($help) . '\'';
			
			$params = explode('/', $key);
			$cnt = count($params);
			$name = $params[$cnt-1];

			$parentKey = implode ('/', array_slice ($params, 0, $cnt-1));
			$parentId = $this->getId ($parentKey);
			
			$db = DBController::getInstance ();
			$db->query ("INSERT INTO `explay_regedit` (`id`, `parent`, `name`, `value`, `help`) VALUES (0, " . $parentId . ", '" . $name . "', " . $value . ", " . $help . ")");
			$db->query ("COMMIT");
		}
		
		$this->clearCache ();

		return true;
	}

	/**
	 * Очистка физического кеша
	 * @return void
	 */
	public function clearCache () {
		if (!$this->init) {
			$this->init ();
		}
		
		$fileRelations = ENGINE_ROOT . '/cache/regeditRelations.php';
		if (file_exists ($fileRelations)) {
			unlink ($fileRelations);
		}
		
		$fileValues = ENGINE_ROOT . '/cache/regeditValues.php';
		if (file_exists ($fileValues)) {
			unlink ($fileValues);
		}
		
		$fileHelpers = ENGINE_ROOT . '/cache/regeditHelpers';
		if (file_exists ($fileHelpers)) {
			unlink ($fileHelpers);
		}
	}
	
	/**
	 * Возвращает ключи и их подсказки
	 * @return array
	 */
	public function getHelpers () {
		if (!$this->init) {
			$this->init ();
		}
		
		if (!empty ($this->aHelpers)) {
			return $this->aHelpers;
		}
		
		$file = ENGINE_ROOT . '/cache/regeditHelpers';

		if (file_exists ($file) && time () - filemtime ($file) < 86400) {
			$cache = file_get_contents ($file);
			return $this->aHelpers = unserialize ($cache);
		} else {
			$db = DBController::getInstance ();
	
			$get = $db->query ("SELECT `parent`, `name`, `help` FROM `explay_regedit` WHERE `help` != ''");
	
			$aKeys = array ();
			$aHelps = array ();
			
			while (list ($parent, $name, $help) = $db->fetch_row ($get)) {
				$aPath = array ($name);
				
				if ($parent) {
					$this->getKeyName ($parent, $aPath);
				}
				
				$aKeys[] = implode ('/', array_reverse ($aPath));
				$aHelps[] = $help;
			}
			
			natsort ($aKeys);
			
			$this->aHelpers = array ();
			
			foreach ($aKeys as $id => $key) {
				$this->aHelpers[]['item'] = array (
					'key' => $key,
					'help' => $aHelps[$id]
				);
			}
			
			$f = fopen ($file, 'w+');
			flock ($f, LOCK_EX);
			fwrite ($f, serialize ($this->aHelpers));
			flock ($f, LOCK_UN);
			fclose ($f);
			
			return $this->aHelpers;
		}		
	}
	
	private function getKeyName ($id, &$aPath) {
		$db = DBController::getInstance ();
		
		$get = $db->query ("SELECT `parent`, `name` FROM `explay_regedit` WHERE `id` = " . $id);
		
		while (list ($parent, $name) = $db->fetch_row ($get)) {
			$aPath[] = $name;
			
			if ($parent) {
				$this->getKeyName ($parent, $aPath);
			}
		}
	}

	/**
	 * Возвращает id параметра
	 * @param string имя переменной
	 * @return integer
	 */
	private function getId ($key) {
		if (!empty ($this->aIdsCache[$key])) {
			return $this->aIdsCache[$key];
		}
		
		$key = trim ($key, '/');
		$params = explode ('/', $key);
		$cnt = count ($params);

		if ($cnt == 0) {
			return 0;
		}

		$id = 0;
		
		for ($i=0; $i<$cnt; ++$i) {
			$param = trim ($params[$i]);
			if ($param != '') {
				$sKey = $id . ':' . $param;
				if (isset ($this->aRelations[$sKey])) {
					$id = $this->aRelations[$sKey];
				} else {
					$id = 0;
				}
			}
		}

		$this->aIdsCache[$key] = $id;
		
		return $id;
	}
	
	private function init () {
		$fileRelaions = ENGINE_ROOT . '/cache/regeditRelations.php';
		$fileValues = ENGINE_ROOT . '/cache/regeditValues.php';

		if (file_exists ($fileRelaions)) {
			include $fileRelaions;
			
			if (!empty ($REGEDIT_RELATIONS)) {
				$this->aRelations = $REGEDIT_RELATIONS;
			} else {
				$this->loadRelations ($fileRelaions);
			}
		} else {
			$this->loadRelations ($fileRelaions);
		}
		
		if (file_exists ($fileValues)) {
			include $fileValues;
			
			if (!empty ($REGEDIT_VALUES)) {
				$this->aValues = $REGEDIT_VALUES;
			} else {
				$this->loadValues ($fileValues);
			}
		} else {
			$this->loadValues ($fileValues);
		}
		
		$this->init = true; 
	}
	
	private function loadRelations ($fileRelaions) {
		$db = DBController::getInstance ();
		$get = $db->query ("SELECT `id`, `parent`, `name` FROM `explay_regedit`");

		if ($db->num_rows ($get) > 0) {
			while (list ($id, $parent, $param) = $db->fetch_row ($get)) {
				$key = $parent . ':' . $param;
				$this->aRelations[$key] = $id;
			}
		}
		
		$f = fopen ($fileRelaions, 'w+');
		flock ($f, LOCK_EX);
		fwrite ($f, $this->array2PHPString ($this->aRelations, 'REGEDIT_RELATIONS'));
		flock ($f, LOCK_UN);
		fclose ($f);
	}
	
	private function loadValues ($fileValues) {
		$db = DBController::getInstance ();
		$get = $db->query ("SELECT `id`, `value` FROM `explay_regedit`");

		if ($db->num_rows ($get) > 0) {
			while (list ($id, $value) = $db->fetch_row ($get)) {
				$this->aValues[$id] = $value;
			}
		}
		
		$f = fopen ($fileValues, 'w+');
		flock ($f, LOCK_EX);
		fwrite ($f, $this->array2PHPString ($this->aValues, 'REGEDIT_VALUES'));
		flock ($f, LOCK_UN);
		fclose ($f);
	}
	
	private function array2PHPString (array $arr, $arrName) {
		$s = <<<TEXT
<?php
\${$arrName} = array (

TEXT;

		$cntElements = count ($arr);
		$i = 1;
		
		foreach ($arr as $key => $value) {
			$s .= "'" . $key . "' => '" . addslashes($value) . "'";
			
			if ($i != $cntElements) {
				$s .= ",\r\n";
			}
			
			++$i;
		}
		
		$s .= ");\r\n";
		
		return $s;
	}
}